// internal/service/file_service.go
package service

import (
	"crypto/sha256"
	"encoding/hex"
	"errors"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/jfastfiler/internal/dao"
	"github.com/jfastfiler/internal/model"
	"github.com/jfastfiler/pkg/security"
	"github.com/jfastfiler/pkg/storage"
)

type FileService struct {
	fileDAO      *dao.FileMetadataDAO
	permDAO      *dao.PermissionDAO
	auditDAO     *dao.AuditDAO
	storage      storage.Storage
	virusScanner *security.VirusScanner
	storageDir   string
	tempDir      string
	maxSize      int64
	enableEncrypt bool
}

func NewFileService(fileDAO *dao.FileMetadataDAO, permDAO *dao.PermissionDAO,
	auditDAO *dao.AuditDAO, storage storage.Storage, virusScanner *security.VirusScanner,
	storageDir, tempDir string, maxSize int64, enableEncrypt bool) *FileService {

	os.MkdirAll(storageDir, 0755)
	os.MkdirAll(tempDir, 0755)

	return &FileService{
		fileDAO:       fileDAO,
		permDAO:       permDAO,
		auditDAO:      auditDAO,
		storage:       storage,
		virusScanner:  virusScanner,
		storageDir:    storageDir,
		tempDir:       tempDir,
		maxSize:       maxSize,
		enableEncrypt: enableEncrypt,
	}
}

type FileListRequest struct {
	Path     string `form:"path" binding:"required"`
	Page     int    `form:"page,default=1"`
	PageSize int    `form:"page_size,default=50"`
	Sort     string `form:"sort,default=name"`
	Order    string `form:"order,default=asc"`
	Name     string `form:"name"`
	FileType string `form:"file_type"`
}

type FileListResponse struct {
	Path      string             `json:"path"`
	Files     []model.FileMetadata `json:"files"`
	Total     int64              `json:"total"`
	PageInfo  PageInfo           `json:"page_info"`
}

type PageInfo struct {
	Page      int `json:"page"`
	PageSize  int `json:"page_size"`
	TotalPages int `json:"total_pages"`
}

type UploadRequest struct {
	Path        string                `form:"path" binding:"required"`
	File        *multipart.FileHeader `form:"file" binding:"required"`
	Description string                `form:"description"`
	Tags        []string              `form:"tags"`
	UserID      int
	Overwrite   bool `form:"overwrite,default=false"`
}

type UploadResponse struct {
	FileID  int    `json:"file_id"`
	UUID    string `json:"uuid"`
	Path    string `json:"path"`
	Size    int64  `json:"size"`
	Message string `json:"message"`
}

type BatchOperation struct {
	Operation string   `json:"operation" binding:"required,oneof=delete move copy rename"`
	Files     []string `json:"files" binding:"required"`
	Target    string   `json:"target,omitempty"`
	NewName   string   `json:"new_name,omitempty"`
}

func (s *FileService) ListFiles(userID int, req *FileListRequest) (*FileListResponse, error) {
	if !s.checkPermission(userID, req.Path, "view") {
		return nil, errors.New("没有查看权限")
	}

	parentFile, err := s.fileDAO.GetByPath(req.Path)
	if err != nil {
		return nil, errors.New("目录不存在")
	}

	queryParams := &dao.FileQueryParams{
		Name:     req.Name,
		FileType: req.FileType,
		Page:     req.Page,
		PageSize: req.PageSize,
		Sort:     req.Sort,
		Order:    req.Order,
	}

	files, total, err := s.fileDAO.ListByParent(parentFile.ID, queryParams)
	if err != nil {
		return nil, err
	}

	// 过滤有权限的文件
	var accessibleFiles []model.FileMetadata
	for _, file := range files {
		if s.checkPermission(userID, file.Path, "view") {
			accessibleFiles = append(accessibleFiles, file)
		}
	}

	totalPages := (int(total) + req.PageSize - 1) / req.PageSize

	return &FileListResponse{
		Path:     req.Path,
		Files:    accessibleFiles,
		Total:    total,
		PageInfo: PageInfo{
			Page:      req.Page,
			PageSize:  req.PageSize,
			TotalPages: totalPages,
		},
	}, nil
}

func (s *FileService) Upload(req *UploadRequest) (*UploadResponse, error) {
	if !s.checkPermission(req.UserID, req.Path, "edit") {
		return nil, errors.New("没有上传权限")
	}

	if req.File.Size > s.maxSize {
		return nil, errors.New("文件大小超过限制")
	}

	// 检查文件是否已存在
	fullPath := filepath.Join(req.Path, req.File.Filename)
	existingFile, err := s.fileDAO.GetByPath(fullPath)
	if err == nil && !req.Overwrite {
		return nil, errors.New("文件已存在")
	}

	// 病毒扫描
	if s.virusScanner != nil && s.virusScanner.Enabled {
		src, err := req.File.Open()
		if err != nil {
			return nil, err
		}
		defer src.Close()

		infected, err := s.virusScanner.ScanStream(src, req.File.Filename)
		if err != nil {
			return nil, errors.New("病毒扫描失败: " + err.Error())
		}
		if infected {
			s.auditDAO.Create(&model.AuditLog{
				UserID:  req.UserID,
				Action:  "virus_detected",
				Path:    fullPath,
				Details: "检测到病毒文件: " + req.File.Filename,
				Status:  "failed",
			})
			return nil, errors.New("文件包含病毒，已拒绝上传")
		}
	}

	// 保存文件
	filePath := filepath.Join(s.storageDir, fullPath)
	os.MkdirAll(filepath.Dir(filePath), 0755)

	src, err := req.File.Open()
	if err != nil {
		return nil, err
	}
	defer src.Close()

	dst, err := os.Create(filePath)
	if err != nil {
		return nil, err
	}
	defer dst.Close()

	if _, err = io.Copy(dst, src); err != nil {
		return nil, err
	}

	// 计算文件哈希
	hash, err := s.calculateFileHash(filePath)
	if err != nil {
		return nil, err
	}

	// 文件加密
	isEncrypted := false
	encryptionKey := ""
	if s.enableEncrypt {
		// 这里实现文件加密逻辑
		// encryptionKey = generateEncryptionKey()
		// err = s.encryptFile(filePath, encryptionKey)
		// if err != nil {
		//     return nil, err
		// }
		isEncrypted = true
	}

	// 保存元数据
	parentFile, _ := s.fileDAO.GetByPath(req.Path)
	tagsJSON := "[]"
	if len(req.Tags) > 0 {
		// 将标签数组转换为JSON
		tagsJSON = `["` + strings.Join(req.Tags, `","`) + `"]`
	}

	var fileID int
	if existingFile != nil && req.Overwrite {
		// 更新现有文件
		existingFile.Size = req.File.Size
		existingFile.ModTime = time.Now()
		existingFile.Checksum = hash
		existingFile.Description = req.Description
		existingFile.Tags = tagsJSON
		existingFile.IsEncrypted = isEncrypted
		existingFile.EncryptionKey = encryptionKey
		existingFile.Version += 1

		err = s.fileDAO.Update(existingFile)
		fileID = existingFile.ID
	} else {
		// 创建新文件记录
		metadata := &model.FileMetadata{
			UUID:          generateUUID(),
			Path:          fullPath,
			Name:          req.File.Filename,
			Size:          req.File.Size,
			IsDir:         false,
			FileExt:       strings.ToLower(filepath.Ext(req.File.Filename)),
			MimeType:      req.File.Header.Get("Content-Type"),
			ModTime:       time.Now(),
			CreateTime:    time.Now(),
			OwnerID:       req.UserID,
			ParentID:      parentFile.ID,
			Checksum:      hash,
			Description:   req.Description,
			Tags:          tagsJSON,
			IsEncrypted:   isEncrypted,
			EncryptionKey: encryptionKey,
		}

		err = s.fileDAO.Create(metadata)
		fileID = metadata.ID
	}

	if err != nil {
		return nil, err
	}

	// 记录操作日志
	s.auditDAO.Create(&model.AuditLog{
		UserID:  req.UserID,
		Action:  "upload",
		Path:    fullPath,
		FileID:  &fileID,
		Details: "文件上传成功，大小: " + formatFileSize(req.File.Size),
		Status:  "success",
	})

	return &UploadResponse{
		FileID:  fileID,
		UUID:    generateUUID(),
		Path:    fullPath,
		Size:    req.File.Size,
		Message: "文件上传成功",
	}, nil
}

func (s *FileService) CreateDirectory(userID int, path, name string) error {
	fullPath := filepath.Join(path, name)
	if !s.checkPermission(userID, path, "edit") {
		return errors.New("没有创建目录权限")
	}

	// 检查目录是否已存在
	_, err := s.fileDAO.GetByPath(fullPath)
	if err == nil {
		return errors.New("目录已存在")
	}

	parentFile, err := s.fileDAO.GetByPath(path)
	if err != nil {
		return errors.New("父目录不存在")
	}

	// 创建物理目录
	dirPath := filepath.Join(s.storageDir, fullPath)
	if err := os.MkdirAll(dirPath, 0755); err != nil {
		return err
	}

	// 创建目录记录
	metadata := &model.FileMetadata{
		UUID:       generateUUID(),
		Path:       fullPath,
		Name:       name,
		IsDir:      true,
		ModTime:    time.Now(),
		CreateTime: time.Now(),
		OwnerID:    userID,
		ParentID:   parentFile.ID,
	}

	if err := s.fileDAO.Create(metadata); err != nil {
		// 回滚：删除已创建的目录
		os.RemoveAll(dirPath)
		return err
	}

	s.auditDAO.Create(&model.AuditLog{
		UserID:  userID,
		Action:  "create_dir",
		Path:    fullPath,
		Details: "创建目录: " + name,
		Status:  "success",
	})

	return nil
}

func (s *FileService) BatchOperation(userID int, op *BatchOperation) error {
	for _, filePath := range op.Files {
		if !s.checkPermission(userID, filePath, strings.ToLower(op.Operation)) {
			return errors.New("没有操作权限: " + filePath)
		}
	}

	switch op.Operation {
	case "delete":
		return s.batchDelete(userID, op.Files)
	case "move":
		return s.batchMove(userID, op.Files, op.Target)
	case "copy":
		return s.batchCopy(userID, op.Files, op.Target)
	case "rename":
		if len(op.Files) != 1 || op.NewName == "" {
			return errors.New("重命名操作需要指定一个新文件名")
		}
		return s.renameFile(userID, op.Files[0], op.NewName)
	default:
		return errors.New("不支持的操作类型")
	}
}

func (s *FileService) batchDelete(userID int, files []string) error {
	for _, filePath := range files {
		metadata, err := s.fileDAO.GetByPath(filePath)
		if err != nil {
			return err
		}

		// 如果是目录，递归删除子文件和子目录
		if metadata.IsDir {
			if err := s.deleteDirectoryRecursive(userID, metadata); err != nil {
				return err
			}
		} else {
			// 物理删除文件
			fullPath := filepath.Join(s.storageDir, filePath)
			if err := os.Remove(fullPath); err != nil && !os.IsNotExist(err) {
				return err
			}
		}

		// 软删除元数据
		if err := s.fileDAO.SoftDelete(metadata.ID, userID); err != nil {
			return err
		}

		s.auditDAO.Create(&model.AuditLog{
			UserID:  userID,
			Action:  "delete",
			Path:    filePath,
			FileID:  &metadata.ID,
			Details: "删除文件/目录",
			Status:  "success",
		})
	}
	return nil
}

func (s *FileService) deleteDirectoryRecursive(userID int, dir *model.FileMetadata) error {
	// 获取目录下的所有文件和子目录
	queryParams := &dao.FileQueryParams{
		Page:     1,
		PageSize: 1000,
	}
	
	children, _, err := s.fileDAO.ListByParent(dir.ID, queryParams)
	if err != nil {
		return err
	}

	for _, child := range children {
		fullPath := filepath.Join(dir.Path, child.Name)
		if child.IsDir {
			if err := s.deleteDirectoryRecursive(userID, &child); err != nil {
				return err
			}
		} else {
			// 删除文件
			physicalPath := filepath.Join(s.storageDir, fullPath)
			if err := os.Remove(physicalPath); err != nil && !os.IsNotExist(err) {
				return err
			}
		}
		
		// 软删除元数据
		if err := s.fileDAO.SoftDelete(child.ID, userID); err != nil {
			return err
		}
	}

	// 删除目录本身
	dirPath := filepath.Join(s.storageDir, dir.Path)
	if err := os.RemoveAll(dirPath); err != nil {
		return err
	}

	return nil
}

func (s *FileService) batchMove(userID int, files []string, target string) error {
	targetDir, err := s.fileDAO.GetByPath(target)
	if err != nil {
		return errors.New("目标目录不存在")
	}

	if !targetDir.IsDir {
		return errors.New("目标路径不是目录")
	}

	if !s.checkPermission(userID, target, "edit") {
		return errors.New("没有目标目录的写入权限")
	}

	for _, filePath := range files {
		metadata, err := s.fileDAO.GetByPath(filePath)
		if err != nil {
			return err
		}

		newPath := filepath.Join(target, metadata.Name)
		
		// 检查目标路径是否已存在
		_, err = s.fileDAO.GetByPath(newPath)
		if err == nil {
			return errors.New("目标路径已存在: " + newPath)
		}

		// 移动物理文件
		oldPhysicalPath := filepath.Join(s.storageDir, filePath)
		newPhysicalPath := filepath.Join(s.storageDir, newPath)
		
		if err := os.Rename(oldPhysicalPath, newPhysicalPath); err != nil {
			return err
		}

		// 更新数据库记录
		if err := s.fileDAO.Move(metadata.ID, newPath, targetDir.ID); err != nil {
			// 回滚移动操作
			os.Rename(newPhysicalPath, oldPhysicalPath)
			return err
		}

		s.auditDAO.Create(&model.AuditLog{
			UserID:  userID,
			Action:  "move",
			Path:    filePath,
			FileID:  &metadata.ID,
			Details: "移动到: " + newPath,
			Status:  "success",
		})
	}

	return nil
}

func (s *FileService) batchCopy(userID int, files []string, target string) error {
	// 实现文件复制逻辑
	return errors.New("复制功能待实现")
}

func (s *FileService) renameFile(userID int, filePath, newName string) error {
	metadata, err := s.fileDAO.GetByPath(filePath)
	if err != nil {
		return err
	}

	dir := filepath.Dir(filePath)
	newPath := filepath.Join(dir, newName)

	// 检查新路径是否已存在
	_, err = s.fileDAO.GetByPath(newPath)
	if err == nil {
		return errors.New("目标文件名已存在")
	}

	// 重命名物理文件
	oldPhysicalPath := filepath.Join(s.storageDir, filePath)
	newPhysicalPath := filepath.Join(s.storageDir, newPath)
	
	if err := os.Rename(oldPhysicalPath, newPhysicalPath); err != nil {
		return err
	}

	// 更新数据库记录
	if err := s.fileDAO.Rename(metadata.ID, newName); err != nil {
		// 回滚重命名操作
		os.Rename(newPhysicalPath, oldPhysicalPath)
		return err
	}

	s.auditDAO.Create(&model.AuditLog{
		UserID:  userID,
		Action:  "rename",
		Path:    filePath,
		FileID:  &metadata.ID,
		Details: "重命名为: " + newName,
		Status:  "success",
	})

	return nil
}

func (s *FileService) checkPermission(userID int, path string, action string) bool {
	perm, err := s.permDAO.GetUserPermissions(userID, path)
	if err != nil {
		return false
	}

	switch action {
	case "view":
		return perm.CanView
	case "download":
		return perm.CanDownload
	case "edit":
		return perm.CanEdit
	case "delete":
		return perm.CanDelete
	case "share":
		return perm.CanShare
	case "preview":
		return perm.CanPreview
	default:
		return false
	}
}

func (s *FileService) calculateFileHash(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()

	hasher := sha256.New()
	if _, err := io.Copy(hasher, file); err != nil {
		return "", err
	}

	return hex.EncodeToString(hasher.Sum(nil)), nil
}

func generateUUID() string {
	return hex.EncodeToString([]byte(time.Now().Format("20060102150405.000000000")))[:32]
}

func formatFileSize(size int64) string {
	const unit = 1024
	if size < unit {
		return fmt.Sprintf("%d B", size)
	}
	div, exp := int64(unit), 0
	for n := size / unit; n >= unit; n /= unit {
		div *= unit
		exp++
	}
	return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp])
}